import java.util.StringTokenizer;

import org.ruslan.text.Highlighter;

import junit.framework.TestCase;

public class HighlighterTest extends TestCase {

	private static final String TEST_TEXT = "I like fish and pizza. Little star's deep dish pizza sure is fantastic. Dogs are funny.";
	private static final String TEST_TEXT_LONGEST_WORD = "fantastic.";
	private static final String TEST_QUERY = "deep dish pizza";
	private static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras id erat massa. Ullamcorper Lorem Sed ipsum massa risus massa sed id Lorem, ullamcorper nec sollicitudin id, congue sed tortor. Phasellus sed enim leo. Nullam vehicula varius faucibus. Vestibulum augue mi, adipiscing ac sagittis ut amet.";

	private String beginTag;
	private String endTag;
	private int maxSnippetSize;

	@Override
	protected void setUp() throws Exception {
		beginTag = "<em>";
		endTag = "</em>";
		maxSnippetSize = 80;
	}

	/**
	 * case when every word in the query fits snippet size
	 */
	public void testEveryWordIsHighlighted() {
		String query = TEST_QUERY;
		String text = TEST_TEXT;
		Highlighter highlighter = new Highlighter(query);
		highlighter.setTags(beginTag, endTag);
		String highlighted = highlighter.highlight(text, maxSnippetSize);

		StringTokenizer queryTokenizer = new StringTokenizer(query);
		while (queryTokenizer.hasMoreElements()) {
			String token = (String) queryTokenizer.nextElement();
			int countSubstrings = countSubstrings(highlighted, token);
			boolean condition = countSubstrings == countSubstrings(highlighted, tagify(token));
			assertTrue("expected " + countSubstrings + " highlights for [" + token + "]", condition);
		}
	}
	
	/**
	 * case when query words do not fit max snippet size
	 */
	public void testHighlightBigSpan() {
		String text = TEST_TEXT + " " + LOREM_IPSUM + " apples are red";
		String query = TEST_QUERY + " apples";

		Highlighter highlighter = new Highlighter(query);
		highlighter.setTags(beginTag, endTag);
		String highlighted = highlighter.highlight(text, maxSnippetSize);

		StringTokenizer queryTokenizer = new StringTokenizer(query);
		while (queryTokenizer.hasMoreElements()) {
			String token = (String) queryTokenizer.nextElement();
			int countSubstrings = countSubstrings(highlighted, token);
			boolean condition = countSubstrings == countSubstrings(highlighted, tagify(token));
			assertTrue("expected " + countSubstrings + " highlights for [" + token + "]", condition);
			if (token.equals("apples")) {
				assertTrue("apples should not be included", countSubstrings == 0);
			}
			else {
				assertTrue(token + " is expected to be included", countSubstrings > 0);
			}
		}
	}

	/**
	 * case when snippet must be truncated
	 */
	public void testMaxSnippetSize() {
		String query = TEST_QUERY;
		String text = TEST_TEXT;

		Highlighter highlighter = new Highlighter(query);
		highlighter.setTags(beginTag, endTag);
		String highlighted = highlighter.highlight(text, maxSnippetSize);

		String snippet = highlighted.replace(beginTag, "").replace(endTag, "");
		int snippetLength = snippet.length();
		boolean condition = snippetLength <= maxSnippetSize;
		assertTrue("expected <= " + maxSnippetSize + ", actually: " + snippetLength, condition);
	}

	/**
	 * tests that snippet is not truncated too much
	 */
	public void testMinSnippetSize() {
		String query = TEST_QUERY;
		String text = TEST_TEXT;

		Highlighter highlighter = new Highlighter(query);
		highlighter.setTags(beginTag, endTag);
		String highlighted = highlighter.highlight(text, maxSnippetSize);

		String snippet = highlighted.replace(beginTag, "").replace(endTag, "");
		int snippetLength = snippet.length();
		int minLength = maxSnippetSize - TEST_TEXT_LONGEST_WORD.length();
		boolean condition = snippetLength >= minLength;
		assertTrue("expected >= " + minLength + ", actually: " + snippetLength, condition);
	}

	/**
	 * does the same as testHighlight, but differently
	 */
	public void testQueryAll() {
		String query = "a b";
		String text = "aba a ab b";
		Highlighter highlighter = new Highlighter(query);
		highlighter.setTags(beginTag, endTag);
		String highlighted = highlighter.highlight(text, maxSnippetSize);

		String expected = "aba " + tagify("a") + " ab " + tagify("b");
		assertEquals(expected, highlighted);
	}

	/**
	 * case when not words from the query present in the text
	 */
	public void testQueryPartial() {
		String query = "a b c";
		String text = "aba a ab b";
		Highlighter highlighter = new Highlighter(query);
		highlighter.setTags(beginTag, endTag);
		String highlighted = highlighter.highlight(text, maxSnippetSize);

		String expected = "aba " + tagify("a") + " ab " + tagify("b");
		assertEquals(expected, highlighted);
	}

	/**
	 * case when none of the words in the query present in the text
	 */
	public void testQueryNone() {
		String query = "c";
		String text = "aba a ab b";
		Highlighter highlighter = new Highlighter(query);
		highlighter.setTags(beginTag, endTag);
		String highlighted = highlighter.highlight(text, maxSnippetSize);

		assertEquals(text, highlighted);
	}

	/**
	 * tests that the exception is thrown on the wrong input
	 */
	public void testWrongInput() {
		checkWrongInputGeneric("bla bla", "", maxSnippetSize);
		checkWrongInputGeneric("bla bla", "\t \n", maxSnippetSize);
		checkWrongInputGeneric("bla bla", null, maxSnippetSize);
		checkWrongInputGeneric("", "blah blah", maxSnippetSize);
		checkWrongInputGeneric("\t \n", "blah blah", maxSnippetSize);
		checkWrongInputGeneric(null, "blah blah", maxSnippetSize);
	}
	
	private void checkWrongInputGeneric(String query, String text, int maxSnippetSize) {
		try {
			Highlighter highlighter = new Highlighter(query);
			highlighter.highlight(text, maxSnippetSize);
			fail("expecting IllegalArgumentException for: [" + query + "] [" + text + "] " + maxSnippetSize);
		} catch (IllegalArgumentException iae) {}
	}

	private String tagify(String word) {
		return beginTag + word + endTag;
	}

	private static int countSubstrings(String str, String findStr) {
		int count = 0;

		int lastIndex = str.indexOf(findStr);

		while (lastIndex != -1) {
			count++;
			lastIndex = str.indexOf(findStr, lastIndex + findStr.length());
		}

		return count;
	}

	// /**
	// * Count the number of instances of substring within a string.
	// *
	// * @param string
	// * String to look for substring in.
	// * @param substring
	// * Sub-string to look for.
	// * @return Count of substrings in string.
	// */
	// public static int countSubstrings(final String string, final String
	// substring) {
	// int count = 0;
	// int idx = 0;
	//
	// while ((idx = string.indexOf(substring, idx)) != -1) {
	// idx++;
	// count++;
	// }
	//
	// return count;
	// }
}
